`include"definitions.v"
/*
*	My Persudo Prediction Reservation
*	It Records the Aim Address and the Instruction there
*	If A Jump or Branch Instruction is Executed,it will check the reservation whether the aim address is in Reservation
*	If the answer is YES, then push the aim instruction into F/D Register, and add Aim Address for the next Instruction
*	Default Reservation Size is 16, which can be modified in "definitions.v"
*	This Method can reduce one NOP Instruction just following a Jump or Branch Instruction when Executed more than twice
*	It is quite useful in a loop
*/
module PredictionReservation(
	//input
	Instr,
	PCPlus4,
	Branch,// PCSrc in CPU for successful Branch
	Jump,
	PCBranch,
	JumpAddr,
	//for SMC Deal
	ALUOut,
	WriteData,
	Writable,
	//output
	PredictInstr,
	GoOnAddr,
	DoPredict
);

	input[31:0] Instr,PCPlus4,PCBranch,JumpAddr,ALUOut,WriteData;
	input Branch,Jump,Writable;
	
	output[31:0] PredictInstr,GoOnAddr;
	output DoPredict;
	//module Construction
	reg[31:0] AimAddr[`PR_SIZE-1:0],AimInstruction[`PR_SIZE-1:0],PredictInstr,GoOnAddr;
	reg DoPredict,DBPValid[`PR_SIZE-1:0],CanPredict[`PR_SIZE-1:0];
	integer LastUseTime[`PR_SIZE-1:0];
	
	integer i,flag,Current,Choose,Earliest,Replace;
	
	//initial
	initial begin
		for(i = 0 ; i < `PR_SIZE ; i = i +1) begin
			AimAddr[i] = 0;
			AimInstruction[i] = 0;
			DBPValid[i] = 0;
			LastUseTime[i] = 0;
			CanPredict[i] = 0;
		end
		PredictInstr = 0;
		DoPredict = 0;
		Current = 0;
		GoOnAddr = 0;
	end

	//for SMC it may change the AimInstruction in Prediction Reservation
	always @(ALUOut or WriteData or Writable) begin
		for(i = 0 ; i < `PR_SIZE; i = i+1)
			if(Writable == 1 && DBPValid[i] == 1 && ALUOut == AimAddr[i]) begin
				AimInstruction[i] = WriteData;
				CanPredict[i] = 1;
			end
	end
	
	always @(Instr or PCPlus4) begin
		flag = `PR_SIZE;
		// To Refresh AimInstruction and see whether here can do predict
		for(i = 0 ; i < `PR_SIZE ; i = i+1) begin
			if(DBPValid[i] == 1 && PCPlus4 == AimAddr[i]+4) begin
				AimInstruction[i] = Instr;
				CanPredict[i] = 1;
				//$display("Refresh:time = %0d,Choose = %0d,AimInstr = %b,AimAddr = %d,PCPlus4 = %d,Instr = %b",$time,i,AimInstruction[i],AimAddr[i],PCPlus4,Instr);
			end
			if(Branch == 1 && PCBranch == AimAddr[i] && DBPValid[i] == 1) flag = i;
			if(Jump == 1 && JumpAddr == AimAddr[i] && DBPValid[i] == 1) flag = i;
		end
		
		//Decide Whether to Do Predict
		if(flag != `PR_SIZE && CanPredict[flag] == 1) begin
			DoPredict = 1;
			PredictInstr = AimInstruction[flag];
			GoOnAddr = AimAddr[flag] + 4;
			//Refresh Current
			if(LastUseTime[flag] != Current) begin
				Current = Current+1;
				LastUseTime[flag] = Current;
			end
		end
		else DoPredict = 0;
		
		//A new Aim Address will come
		Choose = `PR_SIZE;
		Earliest = Current;
		
		if(flag == `PR_SIZE && (Branch || Jump)) begin
			for(i = 0 ; i < `PR_SIZE ; i = i +1) begin
				if(DBPValid[i] == 0 && Choose == `PR_SIZE) Choose = i;
				if(DBPValid[i] == 1)
					if( LastUseTime[i] < Earliest) begin
						Earliest = LastUseTime[i];
						Replace = i;
					end
			end
			
			if(Choose != `PR_SIZE) begin
				if(Branch == 1) begin
					AimAddr[Choose] = PCBranch;
					CanPredict[Choose] = 0;
					DBPValid[Choose] = 1;
					Current = Current+1;
					LastUseTime[Choose] = Current;
				end
				if(Jump == 1) begin
					AimAddr[Choose] = JumpAddr;
					CanPredict[Choose] = 0;
					DBPValid[Choose] = 1;
					Current = Current+1;
					LastUseTime[Choose] = Current;
				end
			end
			else begin
				if(Branch == 1) begin
					AimAddr[Replace] = PCBranch;
					CanPredict[Replace] = 0;
					DBPValid[Replace] = 1;
					Current = Current+1;
					LastUseTime[Replace] = Current;
				end
				if(Jump == 1) begin
					AimAddr[Replace] = JumpAddr;
					CanPredict[Replace] = 0;
					DBPValid[Replace] = 1;
					Current = Current+1;
					LastUseTime[Replace] = Current;
				end
			end
		end
	end

endmodule




